生存期間 のスコープ
from 項目14:生存期間を理解しよう
スタック 上の要素の 生存期間 は、その要素が同じ場所に存在することが保証されている期間
したがって、その要素の参照(ポインタ)が有効であることが保証されている期間 と等しい
生存期間は要素が生成された時点から始まり、ドロップ(デストラクタ)されるか、ムーブ されるまで続く
【補足】Rust ではスタックと ヒープ 間で頻繁にムーブする
ドロップされる正確なタイミングは、その要素が名前を持つかで変わる
名前を持つ場合
名前を持つ要素の生存期間は、要素が作られてその名前に要素が 束縛 された時点から始まる
ローカル変数の場合: let var = ... で変数が宣言された時点
関数パラメータ: 関数呼び出され、スタックフレーム が設定される時点
生存期間は、要素がムーブされるか名前がスコープから外れる まで続く
e.g.
code:rs
{
let item1 = Item { contents: 1 };
let item2 = Item { contents: 2 };
consuming_fn(item2); // item2 はここでムーブされる
} // item1 はここでドロップされる
名前を持たない場合
不要になった時点 でドロップされる
e.g.
code:rs
let x = f((a + b) * 2);
コンパイラ によって、式がブロックに拡張され、一時変数が挿入されると考えると理解しやすい
code:rs
let x = {
let temp1 = a + b;
{
let temp2 = temp1 * 2;
f(temp2)
} // item2 はここでドロップされる
}; // item1 はここでドロップされる
コンパイラが生存期間を計算する様子を見るには、意図的に 借用チェッカ でエラーが起きるようなコードを書けば良い
名前を持つ場合
code:rs
let r: &Item;
{
let item = Item { contents: 42 };
r = &item;
}
code:_
errorE0597: item does not live long enough(item の生存期間が十分でない)
--> src/main.rs:10:12
|
9 | let item = Item { contents: 42 };
| ---- binding item declared here(item への束縛はここで宣言)
10 | r = &item;
| ^^^^^ borrowed value does not live long enough(借用した値の生存期間が十分でない)
11 | }
| - item dropped here while still borrowed(item は借用中だがここでドロップされる)
12 | println!("r.contents = {}", r.contents);
| ---------- borrow later used here(借用がここで使われている)
名前を持たない場合
code:rs
let r: &Item = fn_returning_ref(&mut Item { contents: 42 });
println!("r.contents = {}", r.contents);
code:_
errorE0716: temporary value dropped while borrowed
--> src/main.rs:7:41
|
7 | let r: &Item = fn_returning_ref(&mut Item { contents: 42 });
| ^^^^^^^^^^^^^^^^^^^^^ - temporary value is freed
| at the end of this statement(一時的な値がこの式の最後に解放される)
| |
| creates a temporary value which is freed while
| still in use(ここで作られた一時的な値が使用中に解放される)
8 | println!("r.contents = {}", r.contents);
| ---------- borrow later used here(借用がここで使われている)
|
help: consider using a let binding to create a longer lived value
(let を用いて生存期間の長い値を作る)
参照の生存期間には ノンレキシカル生存期間 が適用される
これにより、借用ルール は少し緩和される
e.g. 可変参照 がスコープ内に存在するにも関わらず、不変参照を作成できる
本来であれば、同じ生存期間内に可変参照と不変参照の両方を生成することはできない
code:rs
{
let mut s: String = "Hello, world".to_string(); // s は String を所有する
let greeting = &mut s..5; // String への可変参照を作成する
greeting.make_ascii_uppercase();
// これ以降 greeting は使用されないので、ノンレキシカル生存期間によりここでドロップされる
let r: &str = &s; // 可変参照はまだスコープ内にあるが、String への不変参照を作成することが許可される
println!("s = '{r}'");
} // greeting は本来ここでドロップされるはず